#include "BMPImageLoader.h"

namespace ISNW
{
namespace video
{
	bool BMPImageLoader::isLoaderableFile(const c8* fname) const
	{
		if(fname == 0) return false;

		const c8* p = fname;

		u32 len = 0;

		while(*p++) len++;

		if(len <= 4) return false;

		p -= 5;

		if(p[0] == '.' && (p[1] == 'b' || p[1] == 'B') && (p[2] == 'm' || p[2] == 'M') && (p[3] == 'p' || p[3] == 'P')) return true;

		return false;
	}

	IImage* BMPImageLoader::loadImage(io::IReadFile* rfile)
	{
		if(rfile == 0) return 0;

		//--------------
		// read header   
		//--------------

		BMPHeader header;
		rfile->read(&header,sizeof(header));

		if(header.Type != 'MB') return 0; 			// must be BM, we will ignore other types

		if(header.Planes != 1) return 0;			// must be 1

		image = new Image;

		image->BPP = header.BPP;

		if(header.Height < 0) downTop = false;

		image->Dimention = core::dimention2di(header.Width,header.Height > 0 ? header.Height : -header.Height);

		// 4 bytes aligned around
		u32 Pitch = image->BPP * image->Dimention.width;
		image->Pitch = ((Pitch + 255) & ~255) >> 3;



		u32 dataSize = header.SizeImage;

		image->Data = new color32[header.Width * header.Height];

		u32 offBits = header.Offbits;

		rfile->seek(offBits,false);

		// debug
		c8* debug_buffer = 0;
		{
#			if defined ( DEBUG ) || defined( _DEBUG )
			debug_buffer = new c8[dataSize];
			rfile->read(debug_buffer,dataSize);
			rfile->seek(offBits,false);
#			endif
		}

		//--------------
		// read data
		//--------------

		switch(image->BPP)
		{
		case 16:
			u16 redMask,greenMask,blueMask;

			if(header.Compression == BI_RGB)
			{
				redMask = 0x7C00;
				greenMask = 0x03E0;
				blueMask = 0x001F;
			}
			else
			if(header.Compression == BI_BITFIELDS)
			{
				redMask = (u16)(header.Colors[0]);
				greenMask = (u16)(header.Colors[1]);
				blueMask = (u16)(header.Colors[2]);
			}
			else return 0;

			load16(rfile,redMask,greenMask,blueMask);
			break;

		case 24:
			load24(rfile);
			break;

		case 32:
			load32(rfile);
			break;

		case 1:
		case 4:
		case 8:
		default:
			return 0;	// 1,4,8 bpp are not supported

		}

		// debug
		{
#			if defined ( DEBUG ) || defined ( _DEBUG )
			delete[] debug_buffer;
#			endif
		}
		return image;
	}

	void BMPImageLoader::load16(io::IReadFile* rfile,u16 redMask,u16 greenMask,u16 blueMask)
	{

		u16* buffer = (u16*)new c8[image->Pitch];

		if(downTop)
		{
			// down top
			for(s32 i = image->Dimention.height - 1; i >= 0; --i)
			{
				// read a line
				rfile->read(buffer,image->Pitch);

				u32 idx = i*image->Dimention.width;

				for(u32 j = 0; j < image->Dimention.width; ++j)
				{
					u16 color = buffer[j];
					image->Data[idx++].set(color&redMask,color&greenMask,color&blueMask);
				}
			}
		}
		else
		{
			// top down
			for(u32 i = 0; i < image->Dimention.height; ++i)
			{
				// read a line
				rfile->read(buffer,image->Pitch);

				u32 idx = i*image->Dimention.width;

				for(u32 j = 0; j < image->Dimention.width; ++j)
				{
					u16 color = buffer[j];
					image->Data[idx++].set(color&redMask,color&greenMask,color&blueMask);
				}
			}
		}

		delete[] buffer;
	}

	void BMPImageLoader::load24(io::IReadFile* rfile)
	{
		u8* buffer = new u8[image->Pitch];

		if(downTop)
		{
			// down top
			for(s32 i = image->Dimention.height-1; i >= 0; --i)
			{
				// read a line
				rfile->read(buffer,image->Pitch);

				u32 idx_img = i * image->Dimention.width;

				for(u32 j = 0; j < image->Dimention.width; ++j)
				{
					u32 idx_buf = j * 3;
					image->Data[idx_img++].set(buffer[idx_buf+0],buffer[idx_buf+1],buffer[idx_buf+2]);
				}
			}
		}
		else
		{
			// top down
			for(u32 i = 0; i < image->Dimention.height; ++i)
			{
				// read a line
				rfile->read(buffer,image->Pitch);

				u32 idx_img = i * image->Dimention.width;

				for(u32 j = 0; j < image->Dimention.width; ++j)
				{
					u32 idx_buf = j * 3;
					image->Data[idx_img++].set(buffer[idx_buf+0],buffer[idx_buf+1],buffer[idx_buf+2]);
				}
			}

		}

		delete[] buffer;
	}

	void BMPImageLoader::load32(io::IReadFile* rfile)
	{
		u32* buffer =(u32*) new c8[image->Pitch];

		if(downTop)
		{
			// down top
			for(s32 i = image->Dimention.height-1; i >= 0; --i)
			{
				// read a line
				rfile->read(buffer,image->Pitch);

				u32 idx = i * image->Dimention.width;

				for(u32 j = 0; j < image->Dimention.width; ++j)
				{
					image->Data[idx++].set(buffer[j]);
				}
			}
		}
		else
		{
			// top down
			for (u32 i = 0; i < image->Dimention.height; ++i)
			{
				// read a line
				rfile->read(buffer,image->Pitch);

				u32 idx = i * image->Dimention.width;

				for(u32 j = 0; j < image->Dimention.width; ++j)
				{
					image->Data[idx++].set(buffer[j]);
				}
			}
		}

		delete[] buffer;
	}

} // end namespace video
} // end namespace ISNW